#ifndef __LOGGER_HPP_

#define __LOGGER_HPP_

#ifndef _GNU_SOURCE
#define _GNU_SOURCE
#endif

#include "formatter.hpp"
#include "message.hpp"
#include "sink.hpp"
#include "looper.hpp"
#include <memory>
#include <cstdarg>
#include <unordered_map>

namespace Log
{

    class Logger
    {
    protected:
        Format::ptr _format;             // 格式化器
        std::string _logger_name;        // 日志器名称
        LogLevel::value _limit;          // 输出限制
        std::vector<LogSink::ptr> _sink; // 落地方向数组(一个日志器可能落地多个方向)
        WorkType _mode;                  // 工作模式,只有异步日志器设置有效
    public:
        using ptr = std::shared_ptr<Logger>;
        Logger(const std::string &LoggerName, LogLevel::value limit, Format::ptr &format, std::vector<LogSink::ptr> &sink)
            : _format(format), _logger_name(LoggerName), _limit(limit), _sink(sink.begin(), sink.end())
        {
        }
        // 工作模式选择,只有异步日志器设置有效
        void workMode(WorkType mode)
        {
            _mode=mode;
        }
        const std::string &getName()
        {
            return _logger_name;
        }
        void debug(const std::string &file, size_t line, const std::string &fmt, ...)
        {
            // 先创建消息对象,然后格式化,最后落地
            if (LogLevel::DEBUG < _limit)
                return; // 小于限制等级不给输出
            va_list ap;
            va_start(ap, fmt);
            char *res;
            int ret = vasprintf(&res, fmt.c_str(), ap);
            if (ret == -1) // 转换失败
            {
                std::cout << "vasprintf failed!\n";
                return;
            }
            LogMsg msg(LogLevel::DEBUG, _logger_name, file, line, res);
            std::string str = _format->format(msg);
            log(str.c_str(), str.size());
            free(res);
        }

        void info(const std::string &file, size_t line, const std::string &fmt, ...)
        {
            // 先创建消息对象,然后格式化,最后落地
            if (LogLevel::INFO < _limit)
                return; // 小于限制等级不给输出
            va_list ap;
            va_start(ap, fmt);
            char *res;
            int ret = vasprintf(&res, fmt.c_str(), ap);
            if (ret == -1) // 转换失败
            {
                std::cout << "vasprintf failed!\n";
                return;
            }
            LogMsg msg(LogLevel::INFO, _logger_name, file, line, res);
            std::string str = _format->format(msg);
            log(str.c_str(), str.size());
            free(res);
        }

        void warn(const std::string &file, size_t line, const std::string &fmt, ...)
        {
            // 先创建消息对象,然后格式化,最后落地
            if (LogLevel::WARN < _limit)
                return; // 小于限制等级不给输出
            va_list ap;
            va_start(ap, fmt);
            char *res;
            int ret = vasprintf(&res, fmt.c_str(), ap);
            if (ret == -1) // 转换失败
            {
                std::cout << "vasprintf failed!\n";
                return;
            }
            LogMsg msg(LogLevel::WARN, _logger_name, file, line, res);
            std::string str = _format->format(msg);
            log(str.c_str(), str.size());
            free(res);
        }

        void error(const std::string &file, size_t line, const std::string &fmt, ...)
        {
            // 先创建消息对象,然后格式化,最后落地
            if (LogLevel::ERROR < _limit)
                return; // 小于限制等级不给输出
            va_list ap;
            va_start(ap, fmt);
            char *res;
            int ret = vasprintf(&res, fmt.c_str(), ap);
            if (ret == -1) // 转换失败
            {
                std::cout << "vasprintf failed!\n";
                return;
            }
            LogMsg msg(LogLevel::ERROR, _logger_name, file, line, res);
            std::string str = _format->format(msg);
            log(str.c_str(), str.size());
            free(res);
        }

        void fatal(const std::string &file, size_t line, const std::string &fmt, ...)
        {
            // 先创建消息对象,然后格式化,最后落地
            if (LogLevel::FATAL < _limit)
                return; // 小于限制等级不给输出
            va_list ap;
            va_start(ap, fmt);
            char *res;
            int ret = vasprintf(&res, fmt.c_str(), ap);
            if (ret == -1) // 转换失败
            {
                std::cout << "vasprintf failed!\n";
                return;
            }
            LogMsg msg(LogLevel::FATAL, _logger_name, file, line, res);
            std::string str = _format->format(msg);
            log(str.c_str(), str.size());
            free(res);
        }

    protected:
        virtual void log(const char *date, size_t len) = 0;
    };

    class SyncLogger : public Logger
    {
    private:
        virtual void log(const char *date, size_t len)
        {
            for (auto sink : _sink)
            {
                sink->log(date, len);
            }
        }

    public:
        SyncLogger(const std::string &LoggerName, LogLevel::value limit, Format::ptr &format, std::vector<LogSink::ptr> &sink)
            : Logger(LoggerName, limit, format, sink)
        {
        }
    };

    class AsyncLogger : public Logger
    {
    private:
        AsyncLooper _looper; // 异步工作器
    private:
        void running(Buffer &buf) // 异步工作器回调函数
        {
            for (auto sink : _sink)
            {
                sink->log(buf.begin(), buf.readAbleSize());
            }
        }
        virtual void log(const char *date, size_t len)
        {
            _looper.push(date, len);
        }

    public:
        AsyncLogger(const std::string &LoggerName, LogLevel::value limit, Format::ptr &format, std::vector<LogSink::ptr> &sink)
            : Logger(LoggerName, limit, format, sink), _looper(std::bind(&AsyncLogger::running, this, std::placeholders::_1))
        {
            _looper.workMode(_mode);
        }
    };

    //////////////////////////////////////////////////////////////////////////////////
    // 用建造者模式来建造日志器,简化用户操作
    // 建造者基类,可派生出局部建造者和全局建造者,默认建造同步日志器,输出等级为debug
    enum class LoggerTpye
    {
        LOGGER_SYNC,
        LOGGER_ASYNC
    };

    class LoggerBuilder
    {
    protected:
        Format::ptr _format;             // 格式化器
        std::string _logger_name;        // 日志器名称
        LogLevel::value _limit;          // 输出限制
        std::vector<LogSink::ptr> _sink; // 落地方向数组(一个日志器可能落地多个方向)
        LoggerTpye _logger_type;         // 日志器类型(同步or异步)
        WorkType _mode;                  // 工作模式
    public:
        LoggerBuilder(LoggerTpye logger_type = LoggerTpye::LOGGER_SYNC) : _limit(LogLevel::DEBUG), _logger_type(logger_type) {}
        void buildLoggerName(const std::string &name) { _logger_name = name; }
        void buildFormat(const std::string &format) { _format = std::make_shared<Format>(format.c_str()); }
        void buildLimit(LogLevel::value limit) { _limit = limit; }
        template <typename SinkType, typename... Args>
        void buildSink(Args &&...args)
        {
            LogSink::ptr p = SinkFactory::create<SinkType>(std::forward<Args>(args)...);
            _sink.push_back(p);
        }
        void enableUnsafe()
        {
            _mode = WorkType::UNSAFETY;
        }
        virtual Logger::ptr build() = 0;
    };

    class LocalBuilder : public LoggerBuilder
    {
    public:
        LocalBuilder(LoggerTpye logger_type = LoggerTpye::LOGGER_SYNC) : LoggerBuilder(logger_type) {}
        virtual Logger::ptr build()
        {
            assert(!_logger_name.empty()); // 名字不能为空
            if (_format.get() == nullptr)  // 如果没给格式那就按默认格式
            {
                _format = std::make_shared<Format>();
            }
            if (_sink.empty()) // 空的话默认给一个标准输出落地
            {
                LogSink::ptr p = SinkFactory::create<StdoutSink>();
                _sink.push_back(p);
            }
            Logger::ptr p;
            if (_logger_type == LoggerTpye::LOGGER_SYNC)
            {
                p = std::make_shared<SyncLogger>(_logger_name, _limit, _format, _sink);
            }
            else
            {
                p = std::make_shared<AsyncLogger>(_logger_name, _limit, _format, _sink);
            }
            if (_mode == WorkType::UNSAFETY) p->workMode(WorkType::UNSAFETY);
            return p;
        }
    };

    //////////////////////////////////////////////////////////////////////
    // 日志器管理器(单例模式)
    class LoggerManage
    {
    private:
        Logger::ptr _root_logger; // 默认日志器
        std::mutex _mutex;
        std::unordered_map<std::string, Logger::ptr> _loggers; // 日志器数组
    private:
        LoggerManage()
        {
            // 只需指定名字,其他均有默认值
            LocalBuilder b;
            b.buildLoggerName("root");
            _root_logger = b.build();
            addLogger(_root_logger);
        }

    public:
        static LoggerManage &getInstance()
        {
            // c++11后创建静态变量不存在线程安全问题
            // 如果一个线程先进入,在该线程没有完成创建时,其他线程阻塞
            static LoggerManage eton;
            return eton;
        }
        // 添加日志器
        void addLogger(Logger::ptr logger)
        {
            _mutex.lock();
            _loggers[logger->getName()] = logger;
            _mutex.unlock();
        }
        // 获取默认日志器
        Logger::ptr getRootLogger()
        {
            return _root_logger;
        }
        // 判断日志器是否存在
        bool hasLogger(const std::string &name)
        {
            auto it = _loggers.find(name);
            if (it == _loggers.end())
                return false;
            else
                return true;
        }
        // 获取指定名称的日志器
        Logger::ptr getLogger(const std::string &name)
        {
            if (hasLogger(name) == false)
                return Logger::ptr();
            return _loggers[name];
        }
    };
    class GlobalBuilder : public LoggerBuilder
    {
    public:
        GlobalBuilder(LoggerTpye logger_type = LoggerTpye::LOGGER_SYNC) : LoggerBuilder(logger_type) {}
        virtual Logger::ptr build()
        {
            assert(!_logger_name.empty()); // 名字不能为空
            if (_format.get() == nullptr)  // 如果没给格式那就按默认格式
            {
                _format = std::make_shared<Format>();
            }
            if (_sink.empty()) // 空的话默认给一个标准输出落地
            {
                LogSink::ptr p = SinkFactory::create<StdoutSink>();
                _sink.push_back(p);
            }
            Logger::ptr logger;
            if (_logger_type == LoggerTpye::LOGGER_SYNC)
            {
                logger = std::make_shared<SyncLogger>(_logger_name, _limit, _format, _sink);
            }
            else
            {
                logger = std::make_shared<AsyncLogger>(_logger_name, _limit, _format, _sink);
            }
            LoggerManage::getInstance().addLogger(logger);
            if (_mode == WorkType::UNSAFETY) logger->workMode(WorkType::UNSAFETY);
            return logger;
        }
    };
}; // namespace Log

#endif // !__LOGGER_HPP_